/* This file is part of swapper project
 *
 * Copyright (C) 2020 The Swapper Project Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.swapper.json;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

public interface JsonValueMap {
  /**
   * Returns the number of members in this map.
   * If this map contains more than {@link Integer#MAX_VALUE} members,
   * returns {@link Integer#MAX_VALUE}.
   *
   * @return the number of members in this map.
   */
  int size();

  /**
   * Returns {@code true} if the map is empty.
   * that is, there is no members in the map,
   * the number of members in the map is 0.
   *
   * @return {@code true} if the map is empty.
   */
  boolean isEmpty();

  /**
   * Returns {@code true} if this map contains a value for the specified name.
   *
   * @param name name whose presence in this map is to be tested.
   * @return {@code true} if this map contains a value for the specified name.
   */
  boolean containsName(String name);

  /**
   * Returns {@code true} if this map one or more names to the specified value.
   *
   * @param value the value whose presence in this map is to be tested.
   * @return {@code true} if this map one or more names to the specified value.
   */
  boolean containsValue(JsonValue value);

  /**
   * Returns the value to which the specified name is mapped.
   *
   * @param name the name whose associated value is to be returned.
   * @return the value to which the specified name is mapped.
   */
  JsonValue get(String name);

  /**
   * Returns the value to which the specified name is mapped.
   *
   * @param name the name whose associated value is to be returned.
   * @return the value to which the specified name is mapped.
   */
  default JsonValue getNonNull(String name) {
    return Objects.requireNonNull(get(name), "No value for the name exists: " + name);
  }

  /**
   * Returns the value to which the specified name is mapped, or
   * {@code defaultValue} if this map contains no mapping for the name.
   *
   * @param name         the name whose associated value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the value to which the specified name is mapped, or
   * {@code defaultValue} if this map contains no mapping for the name.
   */
  default JsonValue getOrDefault(String name, JsonValue defaultValue) {
    JsonValue value = get(name);
    return value != null || containsName(name) ? value : defaultValue;
  }

  /**
   * Associates the specified value with the specified name in this map.
   *
   * @param name  name with which the specified value is to be associated.
   * @param value the value to be associated with the specified name.
   * @return the previous value associated with name, or null if there was no mapping for name.
   * @throws NullPointerException if the specified value is null.
   */
  JsonValue put(String name, JsonValue value);

  /**
   * Overloading method.
   * Associates the specified {@link Boolean} value with the specified name in this map.
   *
   * @param name  name with which the specified {@link Boolean} value is to be associated.
   * @param value the {@link Boolean} value to be associated with the specified name.
   * @return the previous value associated with name, or null if there was no mapping for name.
   */
  default JsonValue put(String name, Boolean value) {
    return put(name, value == null ? JsonValue.NULL : value ? JsonValue.TRUE : JsonValue.FALSE);
  }

  /**
   * Overloading method.
   * Associates the specified {@link Number} value with the specified name in this map.
   *
   * @param name  name with which the specified {@link Number} value is to be associated.
   * @param value the {@link Number} value to be associated with the specified name.
   * @return the previous value associated with name, or null if there was no mapping for name.
   */
  default JsonValue put(String name, Number value) {
    return put(name, value == null ? JsonValue.NULL : JsonNumber.valueOf(value));
  }

  /**
   * Overloading method.
   * Associates the specified {@link String} value with the specified name in this map.
   *
   * @param name  name with which the specified {@link String} value is to be associated.
   * @param value the {@link String} value to be associated with the specified name.
   * @return the previous value associated with name, or null if there was no mapping for name.
   */
  default JsonValue put(String name, String value) {
    return put(name, value == null ? JsonValue.NULL : JsonString.valueOf(value));
  }

  /**
   * Copies all the members from the specified map to this map.
   *
   * @param members members to be stored in this map.
   * @throws NullPointerException if the specified members is null.
   */
  void puts(Map<String, ? extends JsonValue> members);

  /**
   * Removes the mapping for a name from this map if it is present.
   *
   * @param name name whose mapping is to be removed from the map.
   * @return the previous value associated with name, or null if there was no mapping for name.
   * @throws NullPointerException if the specified members is null.
   */
  JsonValue remove(String name);

  /**
   * Removes all the members from this map.
   * The map will be empty after this call returns.
   */
  void clear();

  /**
   * Returns a Set view of the names contained in this map.
   *
   * @return a Set view of the names contained in this map.
   */
  Set<String> names();

  /**
   * Returns a Collection view of the JSON values contained in this map.
   *
   * @return a collection view of the JSON values contained in this map.
   */
  Collection<JsonValue> values();

  /**
   * Returns a Set view of the members contained in this map.
   *
   * @return a set view of the members contained in this map
   */
  Set<Map.Entry<String, JsonValue>> members();

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Boolean}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Boolean}.
   * @throws NullPointerException if this map contains no mapping for the name.
   * @see JsonValue#toBoolean()
   */
  default Boolean getBoolean(String name) {
    return getNonNull(name).toBoolean();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte}.
   * @throws NullPointerException if this map contains no mapping for the name.
   * @see JsonValue#toByte()
   */
  default Byte getByte(String name) {
    return getNonNull(name).toByte();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte}.
   *
   * @param name  the name whose associated JSON value is to be returned.
   * @param radix the radix to be used while parsing string.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toByte(int)
   */
  default Byte getByte(String name, int radix) {
    return getNonNull(name).toByte(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toShort()
   */
  default Short getShort(String name) {
    return getNonNull(name).toShort();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short}.
   *
   * @param name  the name whose associated JSON value is to be returned.
   * @param radix the radix to be used while parsing string.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toShort(int)
   */
  default Short getShort(String name, int radix) {
    return getNonNull(name).toShort(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toInteger()
   */
  default Integer getInteger(String name) {
    return getNonNull(name).toInteger();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer}.
   *
   * @param name  the name whose associated JSON value is to be returned.
   * @param radix the radix to be used while parsing string.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toInteger(int)
   */
  default Integer getInteger(String name, int radix) {
    return getNonNull(name).toInteger(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toLong()
   */
  default Long getLong(String name) {
    return getNonNull(name).toLong();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long}.
   *
   * @param name  the name whose associated JSON value is to be returned.
   * @param radix the radix to be used while parsing string.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toLong(int)
   */
  default Long getLong(String name, int radix) {
    return getNonNull(name).toLong(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Float}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Float}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toFloat()
   */
  default Float getFloat(String name) {
    return getNonNull(name).toFloat();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Double}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Double}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toDouble()
   */
  default Double getDouble(String name) {
    return getNonNull(name).toDouble();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toBigInteger()
   */
  default BigInteger getBigInteger(String name) {
    return getNonNull(name).toBigInteger();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger}.
   *
   * @param radix the radix to be used while parsing string.
   * @param name  the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toBigInteger(int)
   */
  default BigInteger getBigInteger(String name, int radix) {
    return getNonNull(name).toBigInteger(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigDecimal}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigDecimal}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toBigDecimal()
   */
  default BigDecimal getBigDecimal(String name) {
    return getNonNull(name).toBigDecimal();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.String}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.String}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toString()
   */
  default String getString(String name) {
    return getNonNull(name).toString();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonArray}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonArray}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toJsonArray()
   */
  default JsonArray getJsonArray(String name) {
    return getNonNull(name).toJsonArray();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonObject}.
   *
   * @param name the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonObject}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toJsonObject()
   */
  default JsonObject getJsonObject(String name) {
    return getNonNull(name).toJsonObject();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Enum<E>}.
   * Convert according to the name attribute({@link java.lang.Enum#name()}) of the enumeration.
   *
   * @param enumType the type of enumeration.
   * @param <E>      the generics type of the enumeration.
   * @param name     the name whose associated JSON value is to be returned.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Enum<E>}.
   * @throws NullPointerException if this JSON object contains no mapping for the name.
   * @see JsonValue#toEnum(Class)
   */
  default <E extends Enum<E>> Enum<E> getEnum(String name, Class<E> enumType) {
    return getNonNull(name).toEnum(enumType);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Boolean},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Boolean},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toBoolean()
   */
  default Boolean getBooleanOrDefault(String name, Boolean defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toBoolean();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toByte()
   */
  default Byte getByteOrDefault(String name, Byte defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toByte();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param radix        the radix to be used while parsing string.
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Byte},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toByte(int)
   */
  default Byte getByteOrDefault(String name, int radix, Byte defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toByte(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toShort()
   */
  default Short getShortOrDefault(String name, Short defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toShort();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param radix        the radix to be used while parsing string.
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Short},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toShort(int)
   */
  default Short getShortOrDefault(String name, int radix, Short defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toShort(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toInteger()
   */

  default Integer getIntegerOrDefault(String name, Integer defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toInteger();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param radix        the radix to be used while parsing string.
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Integer},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toInteger(int)
   */

  default Integer getIntegerOrDefault(String name, int radix, Integer defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toInteger(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toLong()
   */
  default Long getLongOrDefault(String name, Long defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toLong();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param radix        the radix to be used while parsing string.
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Long},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toLong(int)
   */
  default Long getLongOrDefault(String name, int radix, Long defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toLong(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Float},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Float},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toFloat()
   */
  default Float getFloatOrDefault(String name, Float defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toFloat();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Double},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Double},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toDouble()
   */
  default Double getDoubleOrDefault(String name, Double defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toDouble();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toBigInteger()
   */
  default BigInteger getBigIntegerOrDefault(String name, BigInteger defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toBigInteger();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param radix        the radix to be used while parsing string.
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigInteger},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toBigInteger(int)
   */
  default BigInteger getBigIntegerOrDefault(String name, int radix, BigInteger defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toBigInteger(radix);
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigDecimal},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.math.BigDecimal},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toBigDecimal()
   */
  default BigDecimal getBigDecimalOrDefault(String name, BigDecimal defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toBigDecimal();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.String},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.String},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toString()
   */
  default String getStringOrDefault(String name, String defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toString();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonArray},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonArray},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toJsonArray()
   */
  default JsonArray getJsonArrayOrDefault(String name, JsonArray defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toJsonArray();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonObject},
   * or default value if this JSON object contains no mapping for the name.
   *
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link JsonObject},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toJsonObject()
   */
  default JsonObject getJsonObjectOrDefault(String name, JsonObject defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toJsonObject();
  }

  /**
   * Returns the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Enum<E>},
   * or default value if this JSON object contains no mapping for the name.
   * Convert according to the name attribute({@link java.lang.Enum#name()}) of the enumeration.
   *
   * @param enumType     the type of enumeration.
   * @param <E>          the generics type of the enumeration.
   * @param name         the name whose associated JSON value is to be returned.
   * @param defaultValue the default mapping of the name.
   * @return the JSON value to which the specified name is mapped,
   * and as the value of type {@link java.lang.Enum<E>},
   * or default value if this JSON object contains no mapping for the name.
   * @see JsonValue#toEnum(Class)
   */
  default <E extends Enum<E>> Enum<E> getEnumOrDefault(String name,
                                                       Class<E> enumType,
                                                       Enum<E> defaultValue) {
    JsonValue value = get(name);
    return value == null ? defaultValue : value.toEnum(enumType);
  }
}
